import json
import os
import shutil
import tornado
from tornado import gen
from tornado import web

import sys
sys.path.append("..")

from .base import BaseHandler
from utils.jsonresult import JsonResult, json_result_encoder
from utils.pathutil import PathBuilder

class LabStorageHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def post(self):
        user = self.get_current_user()
        root_path = PathBuilder.get_labs_dir_for_student(user['accountname'])
        
        request_data = json.loads(self.request.body.decode())
        sub_path = request_data.get('path')
        operation = request_data.get('operation')
        operand = request_data.get('operand')

        json_result = JsonResult()

        call_func = dispatch_map.get(operation)
        if call_func is None:
            json_result.message = '无效的操作'
        else:
            call_func(root_path, sub_path, operand, json_result)

        self.write(json.dumps(json_result, ensure_ascii=True, default=json_result_encoder))
        self.finish() 
    
    # 从标准库中装载实验文件到学生的实验目录中
    @staticmethod
    def load_lab_files(root_path, sub_path, operand, json_result):
        source_dir = PathBuilder.get_library_dir_for_lab(sub_path)
        if not os.path.exists(source_dir):
            json_result.success = False
            json_result.message = '错误的实验路径'
            return
        
        target_dir = os.path.join(root_path, sub_path)

        try:
            # 先删除目标目录(shutil.copytree必须保证目标目录是不存在的)
            if os.path.exists(target_dir):
                shutil.rmtree(target_dir)

            # 目录整体拷贝
            shutil.copytree(source_dir, target_dir)
            
            # 将拷贝后的所有目录和文件的所有者更改为data_root_owner指定的账户
            # data_root_owner = tornado.options.options.data_root_owner
            # os.system('chown -R ' + data_root_owner + ' ' + target_dir)
        except:
            json_result.success = False
            json_result.message = '装载实验文件失败'
        

    # 获取特定学生特定Lab下的所有一级文件和子目录
    @staticmethod
    def list_items(root_path, sub_path, operand, json_result):
        full_dir_path = os.path.join(root_path, sub_path)
        if not os.path.isdir(full_dir_path):
            json_result.success = False
            json_result.message = '错误的目录路径'
            return

        data_items = []
        for item in os.listdir(full_dir_path):
            data_item = {
                'name':item, 
                'path':os.path.join(sub_path, item), 
                'isdir':os.path.isdir(os.path.join(full_dir_path, item))
            }
            data_items.append(data_item)

        json_result.data = data_items


    # 获取被允许的文件后缀名列表
    @staticmethod
    def allowed_file_types(root_path, sub_path, operand, json_result):
        json_result.data = tornado.options.options.allowed_customized_file_types

    # 更改文件或目录的名称
    # 请注意，不能修改文件的后缀名
    @staticmethod
    def rename_item(root_path, sub_path, operand, json_result):
        origin_item_name = operand.get('originName')
        new_item_name = operand.get('newName').strip()

        if origin_item_name == new_item_name:
            return

        origin_item_full_path = os.path.join(root_path, sub_path, origin_item_name)
        if (not origin_item_name) or (not new_item_name) or (not os.path.exists(origin_item_full_path)):
            json_result.success = False
            json_result.message = '错误的文件或目录路径'
            return
        
        # 提取原始文件名中的后缀，添加到新文件名称后
        extension = ''
        extension_index = origin_item_name.rfind('.')
        if extension_index != -1:
            extension = origin_item_name[extension_index:]
        new_item_full_path = os.path.join(root_path, sub_path, new_item_name) + extension

        # 检查是否已经有同名称的文件或目录存在
        if os.path.exists(new_item_full_path):
            json_result.success = False
            json_result.message = '存在同名文件或目录'
            return

        try:
            os.rename(origin_item_full_path, new_item_full_path)
        except:
            json_result.success = False
            json_result.message = '重命名操作失败'
    

    # 删除文件或目录
    @staticmethod
    def delete_item(root_path, sub_path, operand, json_result):
        item_name = operand.get('itemName').strip()
        item_full_path = os.path.join(root_path, sub_path, item_name)
        
        # 检查待删除的文件或目录是否存在
        if (not item_name) or (not os.path.exists(item_full_path)):
            json_result.success = False
            json_result.message = '错误的文件或目录路径'
            return

        try:
            if os.path.isfile(item_full_path):
                os.remove(item_full_path)
            else:
                shutil.rmtree(item_full_path)
        except:
            json_result.success = False
            json_result.message = '删除文件/目录操作失败'
    

    # 新建子目录
    @staticmethod
    def new_folder(root_path, sub_path, operand, json_result):
        folder_name = operand.get('folderName').strip()
        folder_full_path = os.path.join(root_path, sub_path, folder_name)
        
        # 检查待删除的文件或目录是否存在
        if not folder_name:
            json_result.success = False
            json_result.message = '错误的目录名称'
            return
        
        if os.path.exists(folder_full_path):
            json_result.success = False
            json_result.message = '目录已经存在'
            return

        try:
            os.mkdir(folder_full_path)
        except:
            json_result.success = False
            json_result.message = '创建目录操作失败'


# 处理文件上传
class LabStorageUploaderHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def post(self):
        json_result = JsonResult()

        user = self.get_current_user()
        sub_path = self.get_argument('path')

        uploaded_file = self.request.files['file'][0]
        uploaded_file_name = uploaded_file['filename']
        sections = uploaded_file_name.split('.')
        extension =  sections[-1] if len(sections)>1 else ''
        
        # 检查上传的文档是否是允许的后缀名
        allowed_types = tornado.options.options.allowed_customized_file_types
        if extension not in allowed_types:
            json_result.success = False
            json_result.message = "无效的文件类型"
            self.write(json.dumps(json_result, ensure_ascii=True, default=json_result_encoder))
            self.finish()
            return

        # 检查上传的文件名是否在当前目录下已经存在
        root_path = PathBuilder.get_labs_dir_for_student(user['accountname'])
        uploaded_file_full_path = os.path.join(root_path, sub_path, uploaded_file_name)
        try:
            with open(uploaded_file_full_path, 'wb') as saved_file:
                saved_file.write(uploaded_file['body'])
        except:
            json_result.success = False
            json_result.message = "文件上传失败"
            
        self.write(json.dumps(json_result, ensure_ascii=True, default=json_result_encoder))
        self.finish()


# 处理实验打包下载
class DownloadLabFilesHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        user = self.get_current_user()
        sub_path = self.get_argument('path')
        root_path = PathBuilder.get_labs_dir_for_student(user['accountname'])
        lab_full_path = os.path.join(root_path, sub_path)
        
        shutil.make_archive(lab_full_path, 'zip', lab_full_path)
        zip_filepath = lab_full_path + ".zip"
        
        self.set_header ('Content-Type', 'application/octet-stream')
        self.set_header ('Content-Disposition', 'attachment; filename=' + sub_path + ".zip")
        buf_size = 8192
        with open(zip_filepath, 'rb') as f:
            while True:
                data = f.read(buf_size)
                if not data:
                    break
                self.write(data)
        # 删除zip包
        os.remove(zip_filepath)

        self.finish()

dispatch_map = {
    'load_lab_files' : LabStorageHandler.load_lab_files,
    'list_items': LabStorageHandler.list_items,
    'rename_item' : LabStorageHandler.rename_item,
    'allowed_file_types' : LabStorageHandler.allowed_file_types,
    'delete_item' : LabStorageHandler.delete_item,
    'new_folder' : LabStorageHandler.new_folder
}

default_handlers = [
    (r'/labstorage', LabStorageHandler),
    (r'/labstorage_upload', LabStorageUploaderHandler),
    (r'/labstorage_download', DownloadLabFilesHandler)
]